fix(init): escape user-supplied values in specify init output - #3787
Open
jawwad-ali wants to merge 2 commits into
Open
fix(init): escape user-supplied values in specify init output#3787jawwad-ali wants to merge 2 commits into
specify init output#3787jawwad-ali wants to merge 2 commits into
Conversation
commands/init.py interpolated the project name, --integration/--script values
and paths straight into Rich markup f-strings. It was the only CLI command
module without escaping -- extensions, presets, workflows and integrations all
wrap user-controlled display values already.
Two consequences, both reproduced end-to-end through the real CLI:
1. SILENT WRONG OUTPUT. `specify init "proj [v2]"` exits 0 and creates the
directory, but the Next Steps panel prints
1. Go to the project folder: cd proj
Rich ate `[v2]` as a style tag, so the command the user copy-pastes fails.
2. CRASH AFTER SUCCESS. `specify init "app[/red]x"` creates the project and
then dies with MarkupError("closing tag '[/red]' ... doesn't match any open
tag") -> exit 1 with a traceback for work that actually completed.
Wrap the user-controlled display values in rich.markup.escape: project name
(error/warning/conflict/next-steps), project and working paths, the echoed
--integration and --script values, and the agent folder in the gitignore hint.
Display only -- no control flow, exit codes or messages change, and escape is a
no-op for any value without a tag-shaped bracket run.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jawwad-ali
force-pushed
the
fix/init-escape-user-values-in-output
branch
from
July 28, 2026 14:57
3cea41e to
ed24bdb
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Escapes user-controlled specify init values before rendering them with Rich.
Changes:
- Escapes project names, paths, integration/script values, and agent folders.
- Adds regression tests for bracketed values and malformed Rich tags.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/commands/init.py |
Escapes dynamic Rich output values. |
tests/test_init_output_markup.py |
Tests literal rendering and crash prevention. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Medium
mnriem
requested changes
Jul 29, 2026
mnriem
left a comment
Collaborator
There was a problem hiding this comment.
Please address Copilot feedback
Rich-escaping stopped the brackets being swallowed, but the printed command was still unusable for any name containing whitespace: `cd proj v2` is two arguments in every shell. $ cd proj v2 -> /bin/bash: line 1: cd: too many arguments (rc=1) $ cd "proj v2" -> rc=0, lands in "proj v2" Quote it for the host the same way _version._render_argv renders its copy-pasteable installer command: subprocess.list2cmdline on Windows, shlex.quote elsewhere. Windows must use double quotes -- cd 'my project' is a path-not-found in cmd.exe, while cd "my project" is accepted by cmd.exe, PowerShell and Git Bash alike. Names needing no quoting are returned unchanged, so the common case is byte-identical. Shell-quote inner, Rich-escape outer. Tests execute the printed command through a real shell rather than only inspecting the string, and pin that an ordinary name stays unquoted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
commands/init.pyinterpolates the project name, the echoed--integration/--scriptvalues and paths straight into Rich markup f-strings. It is the only CLI command module without escaping —extensions/_commands.py,presets/_commands.py,workflows/_commands.pyandintegrations/_query_commands.pyall wrap user-controlled display values already.Two consequences, both reproduced end-to-end through the real CLI (
CliRunner, offline):1. Silent wrong output.
specify init "proj [v2]"exits 0 and creates the directory, but the Next Steps panel prints:Rich consumed
[v2]as a style tag, so the command the user copy-pastes fails.2. Crash after success.
specify init "app[/red]x"creates the project and then dies:The project was in fact scaffolded, so
initreports failure for work it completed.Fix
Wrap the user-controlled display values in
rich.markup.escape:--forcemerge notice, the Directory Conflict panel, and the Next Stepscd--integrationand--scriptvalues in their invalid-value errors.gitignorehint (it can come from--commands-dir)Display only — no control flow, exit codes or message wording change, and
escapeis a no-op for any value without a tag-shaped bracket run, so existing output is byte-identical.Verification
tests/test_init_output_markup.py: thecdline naming the real directory (2 parametrized names), the closing-tag crash, and the invalid---integrationecho. All 4 fail before this change, pass after.tests/integrations/test_cli.py.tests/test_init_dir.py/test_init_dir_cli.py: unchanged — the 6test_ps_*failures there are pre-existing on this Windows box (verified: identical 6 failures with my change stashed) and are PowerShell-environment issues, not related to this diff.uvx ruff@0.15.0 check src testsclean.AI-assisted: authored with Claude Code. I confirmed reachability first —
specify init "proj [v2]"really does succeed and create the directory — so the brokencdline is what a user actually gets, then verified each failure end-to-end before and after.